#include "Subsystems/BlockBehaviors/SubsystemSignBlockBehavior.h"
#include "Subsystems/SubsystemTerrain.h"

#include "Blocks/WoodenPostedSignBlock.h"
#include "Blocks/WoodenAttachedSignBlock.h"
#include "Blocks/IronPostedSignBlock.h"
#include "Blocks/IronAttachedSignBlock.h"

#include "Managers/BlocksManager.h"
#include "Managers/GameManager.h"

#include "Components/ComponentMiner.h"
#include "Components/ComponentPlayer.h"
#include "Player/PlayerData.h"
#include "Log.h"
#include "GameSingleton.h"
#include "Network/PackageManager.h"
#include "Network/Packages/SignBlockPackage.h"

namespace PocketSurvival
{
    static const int32_t maxTexts = 32;

    SubsystemSignBlockBehavior::SubsystemSignBlockBehavior()
    {
        m_handleBlocks.push_back(WoodenPostedSignBlock::Index());
        m_handleBlocks.push_back(WoodenAttachedSignBlock::Index());
        m_handleBlocks.push_back(IronPostedSignBlock::Index());
        m_handleBlocks.push_back(IronAttachedSignBlock::Index());
    }

    void SubsystemSignBlockBehavior::onBlockRemoved(int32_t value, int32_t newValue, int32_t x, int32_t y, int32_t z)
    {
        std::lock_guard<std::mutex> lk(m_signTextMapMutex);
        Point3 point(x, y, z);
        auto it = m_signTextByPointMap.find(point);
        if (it != m_signTextByPointMap.end())
        {
            m_signTextByPointMap.erase(it);
        }
    }

    void SubsystemSignBlockBehavior::onNeighborBlockChanged(int32_t x, int32_t y, int32_t z, int32_t neighborX, int32_t neighborY, int32_t neighborZ)
    {
        int32_t cellValueFast = m_terrain->getCellValue(x, y, z);
        int32_t num = Terrain::ExtractContents(cellValueFast);
        int32_t data = Terrain::ExtractData(cellValueFast);
        Block *block = GameSingleton::blocksManager->blocks[num];
        if (block->isBlock<AttachedSignBlock*>())
        {
            Point3 point = CellFace::FaceToPoint3(AttachedSignBlock::GetFace(data));
            int32_t x2 = x - point.posX;
            int32_t y2 = y - point.posY;
            int32_t z2 = z - point.posZ;
            int32_t cellValue = m_terrain->getCellValue(x2, y2, z2);
            int32_t cellContents = Terrain::ExtractContents(cellValue);
            if (GameSingleton::blocksManager->blocks[cellContents]->IsCollidable == false)
            {
                m_terrain->destroyCell(0, x, y, z, 0, false);
            }
        }
        else if (block->isBlock<PostedSignBlock*>())
        {
            int32_t num2 = PostedSignBlock::GetHanging(data) ? m_terrain->getCellValue(x, y + 1, z) : m_terrain->getCellValue(x, y - 1, z);

            if (GameSingleton::blocksManager->blocks[Terrain::ExtractContents(num2)]->IsCollidable == false)
            {
                m_terrain->destroyCell(0, x, y, z, 0, false);
            }
        }
    }

    bool SubsystemSignBlockBehavior::onInteract(const TerrainRaycastResult &raycastResult, ComponentMiner *componentMiner)
    {
        if (componentMiner->componentPlayer->playerData != nullptr)
        {
            const Point3 &point = raycastResult.cellFace.point;
        }
        return true;
    }

    void SubsystemSignBlockBehavior::setSignData(const SignTextData &signTextData)
    {
        std::lock_guard<std::mutex> lk(m_signTextMapMutex);
        m_signTextByPointMap[signTextData.point] = signTextData;
        GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<SignBlockPackage>(signTextData));
    }

    bool SubsystemSignBlockBehavior::getSignData(const Point3 &point, SignTextData &signTextData)
    {
        std::lock_guard<std::mutex> lk(m_signTextMapMutex);
        auto it = m_signTextByPointMap.find(point);
        if (it != m_signTextByPointMap.end())
        {
            signTextData = it->second;
            return true;
        }
        return false;
    }

    void SubsystemSignBlockBehavior::load(const nlohmann::json &json)
    {
        m_terrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();

        std::string jsonText;
        if (Utils::LoadTextFile(fmt::format("{}/sign.json", GameSingleton::singletonPtr->getSingleton<GameManager>()->getNowWorldPath()), jsonText))
        {
            nlohmann::json jsonData = nlohmann::json::parse(jsonText);
            for (const nlohmann::json &item : jsonData)
            {
                Point3 point = item["Point"].get<Point3>();
                m_signTextByPointMap[point] = SignTextData();
                SignTextData &signTextData = m_signTextByPointMap[point];

                signTextData.point = point;
                signTextData.url = item["Url"].get<std::string>();

                for (uint8_t index = 0; index < 4; ++index)
                {
                    signTextData.lines[index] = item["Lines"][index].get<std::string>();
                }

                for (uint8_t index = 0; index < 4; ++index)
                {
                    signTextData.colors[index] = item["Colors"][index].get<Color4>();
                }
            }
        }
    }

    void SubsystemSignBlockBehavior::save(nlohmann::json &json)
    {
        nlohmann::json jsonData = nlohmann::json::array();
        for (auto it = m_signTextByPointMap.begin(); it != m_signTextByPointMap.end(); ++it)
        {
            nlohmann::json item;
            item["Point"] = it->second.point;
            item["Url"] = it->second.url;

            item["Lines"].push_back(it->second.lines[0]);
            item["Lines"].push_back(it->second.lines[1]);
            item["Lines"].push_back(it->second.lines[2]);
            item["Lines"].push_back(it->second.lines[3]);

            item["Colors"].push_back(it->second.colors[0]);
            item["Colors"].push_back(it->second.colors[1]);
            item["Colors"].push_back(it->second.colors[2]);
            item["Colors"].push_back(it->second.colors[3]);
            jsonData.push_back(item);
        }
        Utils::SaveTextFile(fmt::format("{}/sign.json", GameSingleton::gameManager->getNowWorldPath()), jsonData.dump());
    }

    void SubsystemSignBlockBehavior::saveToMsgJson(nlohmann::json &jsonData)
    {
        jsonData["Texts"] = nlohmann::json::object();
        int32_t num = 0;
        for (auto it = m_signTextByPointMap.begin(); it != m_signTextByPointMap.end(); ++it)
        {
            nlohmann::json item = nlohmann::json::object();
            MSG_VALUE_SET(item, "Point", "Point3", it->second.point.toString());
            if (it->second.url != "")
            {
                MSG_VALUE_SET(item, "Url", "string", it->second.url);
            }

            MSG_VALUE_SET(item, "Line1", "string", it->second.lines[0]);
            MSG_VALUE_SET(item, "Line2", "string", it->second.lines[1]);
            MSG_VALUE_SET(item, "Line3", "string", it->second.lines[2]);
            MSG_VALUE_SET(item, "Line4", "string", it->second.lines[3]);

            MSG_VALUE_SET(item, "Color1", "Color", it->second.colors[0].toColor3String());
            MSG_VALUE_SET(item, "Color2", "Color", it->second.colors[1].toColor3String());
            MSG_VALUE_SET(item, "Color3", "Color", it->second.colors[2].toColor3String());
            MSG_VALUE_SET(item, "Color4", "Color", it->second.colors[3].toColor3String());

            jsonData["Texts"][std::to_string(num)] = item;
            ++num;
        }
    }

    static const std::string subsystemName = "SignBlockBehavior";
    const std::string &SubsystemSignBlockBehavior::getName() const
    {
        return subsystemName;
    }

}